#ifndef __COSTMAP_2D_H_
#define __COSTMAP_2D_H_

#include <string>
#include <vector>
#include <queue>
#include <boost/thread.hpp>

#include "common/glog/glog.h"
using namespace std;

namespace costmap_2d{

    // 定义静态变量
    static const unsigned char NO_INFORMATION = 255;
    static const unsigned char LETHAL_OBSTACLE = 254;
    static const unsigned char INSCRIBED_INFLATED_OBSTACLE = 253;
    static const unsigned char FREE_SPACE = 0;

    // 定义Costmap2D
    class Costmap2D{
        public:
            Costmap2D();
            Costmap2D(const Costmap2D& map);
            Costmap2D& operator=(const Costmap2D& map);
            
            virtual ~Costmap2D();
            void initMaps(unsigned int width, unsigned int height);
            void deleteMaps();
            void resetMaps();
            void resetMap(unsigned int x0, unsigned int y0, unsigned int xn, unsigned int yn);
            void resizeMap(unsigned int width, unsigned int height, double resolution,
                double origin_x, double origin_y);

            void mapToWorld(unsigned int mx, unsigned int my, double& wx, double& wy) const;        
            bool worldToMap(double wx, double wy, unsigned int& mx, unsigned int& my) const;
            
            void worldToMapEnforceBounds(double wx, double wy, int& mx, int& my) const;

            unsigned int getIndex(unsigned int mx, unsigned int my) const;
            void indexToCells(unsigned int index, unsigned int& mx, unsigned int& my) const;

            unsigned char getCost(unsigned int mx, unsigned int my) const;            
            void setCost(unsigned int mx, unsigned int my, unsigned char cost);

            virtual void updateOrigin(double new_origin_x, double new_origin_y);
            unsigned int cellDistance(double world_dist);

            unsigned char* getCharMap() const;
            double getWidth() const;
            double getHeight() const;
            double getWidthInMeters() const;
            double getHeightInMeters() const;
            double getOriginX() const;
            double getOriginY() const;
            double getResolution() const;

            // Provide a typedef to ease future code maintenance
            typedef boost::recursive_mutex mutex_t;
            mutex_t* getMutex(){
                return access_;
            }

            bool valueIsValid(unsigned int mx, unsigned int my);

        protected:
            unsigned int width_;
            unsigned int height_;  
            double resolution_;
            double origin_x_;
            double origin_y_;
            unsigned char* costmap_;
            unsigned char default_value_; 

            template<typename data_type>
            void copyMapRegion(data_type* source_map, unsigned int sm_lower_left_x, unsigned int sm_lower_left_y,
                                unsigned int sm_size_x, data_type* dest_map, unsigned int dm_lower_left_x,
                                unsigned int dm_lower_left_y, unsigned int dm_size_x, unsigned int region_size_x,
                                unsigned int region_size_y){
                // we'll first need to compute the starting points for each map
                data_type* sm_index = source_map + (sm_lower_left_y * sm_size_x + sm_lower_left_x);
                data_type* dm_index = dest_map + (dm_lower_left_y * dm_size_x + dm_lower_left_x);
            
                // now, we'll copy the source map into the destination map
                for (unsigned int i = 0; i < region_size_y; ++i)
                {
                    memcpy(dm_index, sm_index, region_size_x * sizeof(data_type));
                    sm_index += sm_size_x;
                    dm_index += dm_size_x;
                }
            }

            template<class ActionType>
            inline void raytraceLine(ActionType at, unsigned int x0, unsigned int y0, unsigned int x1, unsigned int y1,
                                     unsigned int max_length = UINT_MAX){
                int dx = x1 - x0;
                int dy = y1 - y0;
            
                unsigned int abs_dx = abs(dx);
                unsigned int abs_dy = abs(dy);
            
                int offset_dx = sign(dx);
                int offset_dy = sign(dy) * width_;
            
                unsigned int offset = y0 * width_ + x0;
            
                // we need to chose how much to scale our dominant dimension, based on the maximum length of the line
                double dist = hypot(dx, dy);
                double scale = (dist == 0.0) ? 1.0 : std::min(1.0, max_length / dist);
            
                // if x is dominant
                if (abs_dx >= abs_dy)
                {
                    int error_y = abs_dx / 2;
                    bresenham2D(at, abs_dx, abs_dy, error_y, offset_dx, offset_dy, offset, (unsigned int)(scale * abs_dx));
                    return;
                }
            
                // otherwise y is dominant
                int error_x = abs_dy / 2;
                bresenham2D(at, abs_dy, abs_dx, error_x, offset_dy, offset_dx, offset, (unsigned int)(scale * abs_dy));
            }

            template<class ActionType>
            inline void bresenham2D(ActionType at, unsigned int abs_da, unsigned int abs_db, int error_b, int offset_a,
                                    int offset_b, unsigned int offset, unsigned int max_length){
                unsigned int end = std::min(max_length, abs_da);
                for (unsigned int i = 0; i < end; ++i)
                {
                    at(offset);
                    offset += offset_a;
                    error_b += abs_db;
                    if ((unsigned int)error_b >= abs_da)
                    {
                    offset += offset_b;
                    error_b -= abs_da;
                    }
                }
                at(offset);
            }

            inline int sign(int x){
              return x > 0 ? 1.0 : -1.0;
            }

            class MarkCell{
                public:
                    MarkCell(unsigned char* costmap, unsigned char value) :
                        costmap_(costmap), value_(value){}
                    inline void operator()(unsigned int offset){
                        costmap_[offset] = value_;
                    }
                private:
                    unsigned char* costmap_;
                    unsigned char value_;
            };
            
        private:
            mutex_t* access_;
            char* cost_translation_table_;


    };
}   // namespace costmap_2d

#endif